﻿Imports System.Data.SqlClient
Imports System.Security.Cryptography
Imports System.Web.Configuration
Imports GuildCMS.Common

Namespace Account
    Public Class Login
        Inherits System.Web.UI.Page

#Region " Database variables "

        ''' <summary>
        ''' Set the database connection variables used throughout this class.
        ''' </summary>
        ''' <remarks>Placed here so they will not need to be typed out each time they are needed.</remarks>
        Private connectionString As String = WebConfigurationManager.ConnectionStrings("SqlConnectionString").ConnectionString()
        Private connection As SqlConnection = New SqlConnection(connectionString)
        Private command As New SqlCommand

#End Region

#Region " Page events "

        ''' <summary>
        ''' Handles the Load event of the Page control.
        ''' </summary>
        ''' <param name="sender">The source of the event.</param>
        ''' <param name="e">The <see cref="System.EventArgs" /> instance containing the event data.</param>
        Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
            ' Set the minimum length for the password for jQuery Validate plugin.
            txtPassword.Attributes.Add("MinLength", CInt(GetSetting("PasswordLength")))
        End Sub

#End Region

#Region " Click event for when the login form is submitted. "

        ''' <summary>
        ''' Handles the Click event of the btnLogin control.
        ''' </summary>
        ''' <param name="sender">The source of the event.</param>
        ''' <param name="e">The <see cref="System.EventArgs" /> instance containing the event data.</param>
        Protected Sub btnLogin_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnLogin.Click
            If Page.IsValid Then

                ' Check that the supplied login name exists.
                Dim totalRows As Integer = Nothing
                Try
                    command = New SqlCommand("sp_public_countLogins", connection)
                    command.CommandType = CommandType.StoredProcedure
                    command.Parameters.Add("@Login", SqlDbType.NVarChar, 25)
                    command.Parameters("@Login").Value = txtLogin.Text.ToLower
                    command.Parameters.Add("@Total", SqlDbType.Int)
                    command.Parameters("@Total").Direction = ParameterDirection.Output
                    connection.Open()
                    command.ExecuteNonQuery()
                    totalRows = CInt(command.Parameters("@Total").Value)
                Catch ex As Exception

                Finally
                    connection.Close()
                End Try

                ' If the login exists retreive the member's id and currently stored password from the database.
                If totalRows > 0 Then
                    Dim storedPassword As String = Nothing
                    Dim memberId As Integer = Nothing
                    Try
                        command = New SqlCommand("sp_public_getMemberIdPassword", connection)
                        command.CommandType = CommandType.StoredProcedure
                        command.Parameters.Add("@Login", SqlDbType.NVarChar, 25)
                        command.Parameters("@Login").Value = txtLogin.Text.ToLower
                        connection.Open()
                        Dim dr As SqlDataReader = command.ExecuteReader()
                        While dr.Read()
                            storedPassword = CStr(dr("Member_Pass"))
                            memberId = CInt(dr("Member_Id"))
                        End While
                    Catch ex As Exception

                    Finally
                        connection.Close()
                    End Try

                    ' Get the salt used to hash this password from the database.
                    Dim salt As String = CStr(GetSetting("PasswordSalt"))

                    ' Now compare the stored password to the one supplied by the visitor.
                    If storedPassword = HashPassword(txtPassword.Text, salt) Then
                        ' Since login and password match generate a random sting to be used as a "cookie code".
                        Dim cookieCode As String = MakeRandomString(25)

                        ' Insert the cookie code as well as mark if the user wishes to be shown as active on the site at this time into the database.
                        Try
                            command = New SqlCommand("sp_public_loginMember", connection)
                            command.CommandType = CommandType.StoredProcedure
                            command.Parameters.Add("@Login", SqlDbType.NVarChar, 25)
                            command.Parameters("@Login").Value = txtLogin.Text.ToLower
                            command.Parameters.Add("@CookieCode", SqlDbType.NChar, 25)
                            command.Parameters("@CookieCode").Value = cookieCode
                            ' If the authenticated member wishes to not have their status shown hide them
                            command.Parameters.Add("@Active", SqlDbType.Bit)
                            If chkHide.Checked = True Then
                                command.Parameters("@Active").Value = False
                            Else
                                command.Parameters("@Active").Value = True
                            End If
                            connection.Open()
                            command.ExecuteNonQuery()
                        Catch ex As Exception

                        Finally
                            connection.Close()
                        End Try

                        ' If the authenticated member wishes to remain logged in set a cookie containing the "cookie code" and associated member id.
                        If chkRemeber.Checked = True Then
                            Dim loginCookie As New HttpCookie("Account")
                            loginCookie.Values.Add("LoggedIn", True)
                            loginCookie.Values.Add("CookieCode", cookieCode)
                            loginCookie.Values.Add("MemberId", memberId)
                            ' Set the expires value to one year into the future.
                            loginCookie.Expires = DateTime.Now.AddYears(1)
                            Response.Cookies.Add(loginCookie)
                        End If

                        ' Get the members main character's id from the database.
                        Dim mainCharacter As Integer = Nothing
                        Try
                            command = New SqlCommand("sp_public_checkMainCharacter", connection)
                            command.CommandType = CommandType.StoredProcedure
                            command.Parameters.Add("@Member_Id", SqlDbType.Int)
                            command.Parameters("@Member_Id").Value = memberId
                            command.Parameters.Add("@MainCharacter", SqlDbType.Int)
                            command.Parameters("@MainCharacter").Direction = ParameterDirection.Output
                            connection.Open()
                            command.ExecuteNonQuery()
                            ' Return the members main character id.
                            mainCharacter = CInt(command.Parameters("@MainCharacter").Value)
                        Catch ex As Exception

                        Finally
                            connection.Close()
                        End Try

                        ' Set the member's id login status and if they are hidden from view in their session.
                        Session("MemberId") = memberId
                        Session("LoggedIn") = True
                        Session("MainCharater") = mainCharacter
                        If chkHide.Checked = True Then
                            Session("Hidden") = True
                        Else
                            Session("Hidden") = False
                        End If

                        ' User is now logged in redirect the user.
                        If Request.QueryString("returnpage") = Nothing Then
                            ' If a redirect page was not supplied in the query string then redirect them to the default page.
                            Response.Redirect("~/Default.aspx")
                        Else
                            ' If a redirect page was supplied in the query string redirect the user to that page.
                            Response.Redirect("~/" & Request.QueryString("returnpage"))
                        End If

                    Else
                        ' The password check failed so display the login form again.

                    End If
                Else
                    ' The login does not exist within the database so display the login form again.

                End If
            End If
        End Sub

#End Region

#Region " Form OnServerValidate functions. "

        ''' <summary>
        ''' Handles the ServerValidate event of the valLogin control.
        ''' </summary>
        ''' <param name="source">The source of the event.</param>
        ''' <param name="args">The <see cref="System.Web.UI.WebControls.ServerValidateEventArgs" /> instance containing the event data.</param>
        Protected Sub valLogin_ServerValidate(ByVal source As Object, ByVal args As ServerValidateEventArgs)
            ' Set args.IsValid to True by default.
            args.IsValid = True

            ' Check that the supplied login is alphanumeric or contain the charaters ".", "_" and "-".
            Dim loginRegex As New Regex("[a-zA-Z0-9._-]")
            If txtLogin.Text = "" Or loginRegex.IsMatch(txtLogin.Text) = False Then
                args.IsValid = False
            End If

            ' Check that the length of the login supplied is acceptable.
            If txtLogin.Text.Length() > 25 And txtLogin.Text.Length() < 3 Then
                args.IsValid = False
            End If
        End Sub

        Protected Sub valPassword_ServerValidate(ByVal source As Object, ByVal args As ServerValidateEventArgs)
            ' Check the length of the password supplied in the txtPassword1 text box.
            If txtPassword.Text.Length() >= CInt(GetSetting("PasswordLength")) And txtPassword.Text.Length() <= 32 Then
                ' If the length is between the correct parameters the check passed.
                args.IsValid = True
            Else
                ' The length is out of parameters so validation fails.
                args.IsValid = False
            End If
        End Sub

#End Region

#Region " Data generation specific functions. "

        ''' <summary>
        ''' Hashes the password supplied via the form.
        ''' </summary>
        ''' <param name="password">The password supplied via the form.</param>
        ''' <param name="salt">The salt used when generating the MD5 hash.</param>
        ''' <returns></returns>
        Private Function HashPassword(ByVal password As String, ByVal salt As String) As String
            ' Create an MD5 hash of the supplied password.
            Dim sourceText As String = salt + password
            Dim asciiEnc As New ASCIIEncoding
            Dim hash As String = Nothing
            Dim byteSourceText() As Byte = asciiEnc.GetBytes(sourceText)
            Dim md5Hash As New MD5CryptoServiceProvider
            Dim byteHash() As Byte = md5Hash.ComputeHash(byteSourceText)
            For Each b As Byte In byteHash
                hash &= b.ToString("x2")
            Next

            ' Return the hashed password
            Return hash
        End Function

        ''' <summary>
        ''' Generates a random activation code.
        ''' </summary>
        ''' <param name="size">The length we want the string to be.</param>
        ''' <returns>Returns the randomly generated activation code user to activate the new account.</returns>
        Private Function MakeRandomString(ByVal size As Integer) As String
            Dim rand As New Random()
            ' Characters we will use to generate this random string.
            Dim allowableChars() As Char = "ABCDEFGHIJKLOMNOPQRSTUVWXYZ0123456789".ToCharArray()

            ' Start generating the random string.
            Dim randomString As String = String.Empty
            For i As Integer = 0 To size - 1
                randomString += allowableChars(rand.Next(allowableChars.Length - 1))
            Next

            ' Return the random string in upper case.
            Return randomString.ToUpper()
        End Function

#End Region

    End Class
End Namespace